#include "minesweeper.h"
#include "ui_minesweeper.h"

Minesweeper::Minesweeper(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::Minesweeper)
{
    ui->setupUi(this);

    //设置窗体
    setWindowTitle("扫雷");
    resize(WindowWidth,WindowHeight);
    setFont(QFont("微软雅黑",10));

    nRow=9;
    nColumn=9;
    nBomb=10;
    sectime=0;
    InitGame();

    //menu初始化
    menu=new QMenu("难度设置",this);
    ui->menubar->addMenu(menu);

    //新游戏按键初始化
    restart=ui->menubar->addAction("新游戏");
    connect(restart,SIGNAL(triggered(bool)),this,SLOT(slot_newGame()));

    //难度按键初始化
    easy=new QAction("简单",this);
    normal=new QAction("中等",this);
    hard=new QAction("困难",this);
    self=new QAction("自定义",this);

    menu->addAction(easy);
    menu->addAction(normal);
    menu->addAction(hard);
    menu->addAction(self);

    connect(easy,SIGNAL(triggered(bool)),this,SLOT(slot_actionEasy_triggered()));
    connect(normal,SIGNAL(triggered(bool)),this,SLOT(slot_actionNormal_triggered()));
    connect(hard,SIGNAL(triggered(bool)),this,SLOT(slot_actionHard_triggered()));
    connect(self,SIGNAL(triggered(bool)),this,SLOT(slot_actionSelf_triggered()));

    //连接俩ui
    DialogSelf=new Dialog(this);
    connect(DialogSelf,SIGNAL(dia_test()),this,SLOT(slot_actionUser_defined_triggered()));

    //增加背景色
    QPalette palette(this->palette());
    palette.setColor(QPalette::Window, QColor(196,211,232));
    this->setPalette(palette);

    //设置计时器
    runtime=new QTimer(this);
    connect(runtime,SIGNAL(timeout()),this,SLOT(slot_sectime()));
}

Minesweeper::~Minesweeper()
{
    delete ui;
}

void Minesweeper::paintEvent(QPaintEvent *event)//绘制界面
{
    QPainter painter(this);

    for(int i=0;i<nRow;i++)//添加图片
    {
        for(int j=0;j<nColumn;j++)
        {
            switch(view[i][j])
            {
            case zero:
                painter.drawPixmap(BlockWidth*j+45,BlockHeight*i+70,BlockWidth,BlockHeight,QPixmap(":/icons/numbers/0.gif"));
                break;
            case one:
                painter.drawPixmap(BlockWidth*j+45,BlockHeight*i+70,BlockWidth,BlockHeight,QPixmap(":/icons/numbers/1.gif"));
                break;
            case two:
                painter.drawPixmap(BlockWidth*j+45,BlockHeight*i+70,BlockWidth,BlockHeight,QPixmap(":/icons/numbers/2.gif"));
                break;
            case three:
                painter.drawPixmap(BlockWidth*j+45,BlockHeight*i+70,BlockWidth,BlockHeight,QPixmap(":/icons/numbers/3.gif"));
                break;
            case four:
                painter.drawPixmap(BlockWidth*j+45,BlockHeight*i+70,BlockWidth,BlockHeight,QPixmap(":/icons/numbers/4.gif"));
                break;
            case five:
                painter.drawPixmap(BlockWidth*j+45,BlockHeight*i+70,BlockWidth,BlockHeight,QPixmap(":/icons/numbers/5.gif"));
                break;
            case six:
                painter.drawPixmap(BlockWidth*j+45,BlockHeight*i+70,BlockWidth,BlockHeight,QPixmap(":/icons/numbers/6.gif"));
                break;
            case seven:
                painter.drawPixmap(BlockWidth*j+45,BlockHeight*i+70,BlockWidth,BlockHeight,QPixmap(":/icons/numbers/7.gif"));
                break;
            case eight:
                painter.drawPixmap(BlockWidth*j+45,BlockHeight*i+70,BlockWidth,BlockHeight,QPixmap(":/icons/numbers/8.gif"));
                break;
            case flag:
                painter.drawPixmap(BlockWidth*j+45,BlockHeight*i+70,BlockWidth,BlockHeight,QPixmap(":/icons/numbers/flag.gif"));
                break;
            case unknown:
                painter.drawPixmap(BlockWidth*j+45,BlockHeight*i+70,BlockWidth,BlockHeight,QPixmap(":/icons/numbers/unknown.gif"));
                break;
            case bomb:
                painter.drawPixmap(BlockWidth*j+45,BlockHeight*i+70,BlockWidth,BlockHeight,QPixmap(":/icons/numbers/bomb.gif"));
                break;
            case ohno:
                painter.drawPixmap(BlockWidth*j+45,BlockHeight*i+70,BlockWidth,BlockHeight,QPixmap(":/icons/numbers/ohno.gif"));
                break;
            case question:
                painter.drawPixmap(BlockWidth*j+45,BlockHeight*i+70,BlockWidth,BlockHeight,QPixmap(":/icons/numbers/question.gif"));
                break;
            default:;
            }
        }
    }
    //强调中间游戏部分
    painter.setPen(Qt::black);
    painter.drawRect(45,70,nColumn*BlockWidth,nRow*BlockHeight);

    painter.drawPixmap(42,WindowHeight-45,78,30,QPixmap(":/icons/numbers/timer.png"));//添加计时器
    painter.drawPixmap(WindowWidth-123,WindowHeight-45,78,30,QPixmap(":/icons/numbers/leftbomb.png"));//添加计数器

    //标记数显示
    QFont font1("微软雅黑",10);
    painter.setPen(Qt::white);
    painter.setBrush(Qt::white);
    painter.drawText(WindowWidth-100,WindowHeight-23,QString::number(LeftBomb));

    //时间显示
    QFont font2("微软雅黑",10);
    painter.setPen(Qt::white);
    painter.setBrush(Qt::white);
    painter.drawText(85,WindowHeight-23,QString::number(sectime));

}

void Minesweeper::mousePressEvent(QMouseEvent *event)//鼠标事件
{
    if(GameOver)
        return;
    //提取所点位置对应的格子
    int y=(event->x()-45)/BlockWidth;
    int x=(event->y()-70)/BlockHeight;

    if(event->button()==Qt::LeftButton)//左键
    {
        if(view[x][y]==unknown)//点击未翻开的格子时
        {
            timeRecord=true;
            if(data[x][y]==bomb)//如果按到炸弹
            {
                if(first)//第一步就踩到炸弹
                {
                    ResetBomb(x,y);
                    view[x][y]=data[x][y];
                    first=false;
                }
                else
                {
                    GameOver=true;
                    for(int i=0;i<nRow;i++)
                    {
                        for(int j=0;j<nColumn;j++)
                        {
                            if(data[i][j]==bomb&&view[i][j]!=flag)//其他炸弹也一起翻开
                                view[i][j]=data[i][j];
                        }
                    }
                    view[x][y]=ohno;
                }
            }
            else if (IsNum(data[x][y]))//如果按到数字
            {
                first=false;//第一次按没踩到炸弹
                view[x][y]=data[x][y];
            }
            else if(data[x][y]==zero)//如果按到空白
            {
                first=false;//第一次按没踩到炸弹
                RevealBlock(x,y);
            }
            repaint();
        }
        else if (IsNum(view[x][y]))//点击数字
        {
            ShowMore(x,y);
            repaint();
        }
        if(timeRecord)
        {
            if (!(runtime->isActive() ))
                runtime->start(1000);//开始计时
            update();
        }

    }
    else if(event->button()==Qt::RightButton)//右键
    {
        //flag,question和unknown切换
        if(view[x][y]==flag)
            view[x][y]=question;
        else if(view[x][y]==unknown)
        {
            view[x][y]=flag;
            LeftBomb--;
        }
        else if(view[x][y]==question)
        {
            view[x][y]=unknown;
            LeftBomb++;
        }
        repaint();
    }
    if(Win())
    {
        if (runtime->isActive() )
            runtime->stop();//停止计时
        QMessageBox::StandardButton ret;
        //弹出选择窗口选择初始化游戏还是退出
        ret=QMessageBox::information(nullptr,"成功","是否开始新游戏？",QMessageBox::Yes,QMessageBox::No);

        if(ret==QMessageBox::Yes)
            InitGame();
        return;
    }
    if(GameOver)
    {
        if (runtime->isActive() )
            runtime->stop();//停止计时
        QMessageBox::StandardButton ret;
        //弹出选择窗口选择初始化游戏还是退出
        ret=QMessageBox::information(nullptr,"失败","是否重新游戏？",QMessageBox::Yes,QMessageBox::No);

        if(ret==QMessageBox::Yes)
            InitGame();
        return;
    }

}

void Minesweeper::InitGame()//游戏初始化（开始一局新游戏）
{
    GameOver=false;

    //初始化界面大小
    WindowHeight=nRow*BlockHeight+115;
    WindowWidth=nColumn*BlockWidth+90;
    resize(WindowWidth,WindowHeight);

    //初始化方格
    for(int i=0;i<nRow;i++)
    {
        for(int j=0;j<nColumn;j++)
        {
            data[i][j]=zero;
            view[i][j]=unknown;
        }
    }
    //设置炸弹
    for(int i=0;i<nBomb;i++)
    {
        int index=QRandomGenerator::global()->generate()%(nRow*nColumn);//炸弹随机编号
        int x=index/nColumn;
        int y=index%nColumn;

        if(data[x][y]==bomb)//避免炸弹位置重复
            i--;
        else
            data[x][y]=bomb;
    }
    //设置数字
    for(int i=0;i<nRow;i++)
    {
        for(int j=0;j<nColumn;j++)
        {
            if(data[i][j]==bomb)
                continue;
            int num=0;
            for (int horizon=-1;horizon<=1;horizon++)
            {
                for(int vertical=-1;vertical<=1;vertical++)
                {
                    if(i+horizon>=0&&i+horizon<nRow&&j+vertical>=0&&j+vertical<nColumn)//防止出界
                    {
                        if(data[i+horizon][j+vertical]==bomb)
                            num++;
                    }
                }
            }
            switch(num)//之后看看能不能和绘图合并
            {
            case 0:
                data[i][j]=zero;
                break;
            case 1:
                data[i][j]=one;
                break;
            case 2:
                data[i][j]=two;
                break;
            case 3:
                data[i][j]=three;
                break;
            case 4:
                data[i][j]=four;
                break;
            case 5:
                data[i][j]=five;
                break;
            case 6:
                data[i][j]=six;
                break;
            case 7:
                data[i][j]=seven;
                break;
            case 8:
                data[i][j]=eight;
                break;
            }
        }
    }
    LeftBomb=nBomb;
    first=true;
    timeRecord=false;

    if(sectime!=0)
    {
        if(runtime->isActive())
            runtime->stop();//如果点开了以后想重新开一局，原来的计时暂停
    }
    sectime=0;
    repaint();
}

void Minesweeper::ResetBomb(int i, int j)//重新排布炸弹
{
    //清除炸弹
    data[i][j]=zero;
    int judge=1;
    while(judge)
    {
        int index=QRandomGenerator::global()->generate()%(nRow*nColumn);//炸弹随机编号
        int x=index/nColumn;
        int y=index%nColumn;
        if(data[x][y]==bomb||(x==i&&y==j))
            judge=1;
        else
        {
            data[x][y]=bomb;
            judge=0;
        }
    }

    //设置数字
    for(int m=0;m<nRow;m++)
    {
        for(int n=0;n<nColumn;n++)
        {
            if(data[m][n]==bomb)
                continue;
            int num=0;
            for (int horizon=-1;horizon<=1;horizon++)
            {
                for(int vertical=-1;vertical<=1;vertical++)
                {
                    if(m+horizon>=0&&m+horizon<nRow&&n+vertical>=0&&n+vertical<nColumn)//防止出界
                    {
                        if(data[m+horizon][n+vertical]==bomb)
                            num++;
                    }
                }
            }
            switch(num)//之后看看能不能和绘图合并
            {
            case 0:
                data[m][n]=zero;
                break;
            case 1:
                data[m][n]=one;
                break;
            case 2:
                data[m][n]=two;
                break;
            case 3:
                data[m][n]=three;
                break;
            case 4:
                data[m][n]=four;
                break;
            case 5:
                data[m][n]=five;
                break;
            case 6:
                data[m][n]=six;
                break;
            case 7:
                data[m][n]=seven;
                break;
            case 8:
                data[m][n]=eight;
                break;
            }
        }
    }
    repaint();
}

void Minesweeper::RevealBlock(int i,int j)
{
    if(GameOver)
        return;
    if(view[i][j]!=unknown)//只有没翻过的格子才能点
        return;
    view[i][j]=data[i][j];//显示底层
    for (int horizon=-1;horizon<=1;horizon++)
    {
        for(int vertical=-1;vertical<=1;vertical++)
        {
            if(horizon==0&&vertical==0)//跳过自己
                continue;
            if(i+horizon>=0&&i+horizon<nRow&&j+vertical>=0&&j+vertical<nColumn)//防止出界
            {
               if(data[i+horizon][j+vertical]==zero)//周围格子底层为空的，跟着一起翻开，依次类推
                {
                   RevealBlock(i+horizon,j+vertical);
                }
                else if(IsNum(data[i+horizon][j+vertical]))//周围格子底层为数字的，只翻开自己，不再看周围
               {
                   view[i+horizon][j+vertical]=data[i+horizon][j+vertical];
               }
            }
        }
    }
}

bool Minesweeper::IsNum(Block a)//判断是数字格
{
    if(a==zero||a==flag||a==unknown||a==bomb)
        return false;
    return true;
}

bool Minesweeper::MarkAll(int i,int j)//判断标出周围所有雷
{
    for (int horizon=-1;horizon<=1;horizon++)
    {
        for(int vertical=-1;vertical<=1;vertical++)
        {
            if(i+horizon>=0&&i+horizon<nRow&&j+vertical>=0&&j+vertical<nColumn)//防止出界
            {
                if(data[i+horizon][j+vertical]==bomb&&view[i+horizon][j+vertical]!=flag)
                    return false;
            }
        }
    }
    return true;
}

void Minesweeper::ShowMore(int i,int j)//按中间数字即可揭开周围非雷格
{
    if(MarkAll(i,j))
    {
        for (int horizon=-1;horizon<=1;horizon++)
        {
            for(int vertical=-1;vertical<=1;vertical++)
            {
                if(i+horizon>=0&&i+horizon<nRow&&j+vertical>=0&&j+vertical<nColumn)//防止出界
                {
                    if(view[i+horizon][j+vertical]!=flag)
                        view[i+horizon][j+vertical]=data[i+horizon][j+vertical];
                }
            }
        }
    }
}

bool Minesweeper::Win()//游戏胜利
{
    for(int i=0;i<nRow;i++)
    {
        for(int j=0;j<nColumn;j++)
        {
            if(view[i][j]==unknown)
                return false;
        }
    }
    return true;
}

void Minesweeper::slot_newGame()
{
    InitGame();
}
void Minesweeper::slot_actionEasy_triggered()
{
    nRow=9;
    nColumn=9;
    nBomb=10;
    InitGame();
}
void Minesweeper::slot_actionNormal_triggered()
{
    nRow=16;
    nColumn=16;
    nBomb=30;
    InitGame();
}
void Minesweeper::slot_actionHard_triggered()
{
    nRow=16;
    nColumn=30;
    nBomb=99;
    InitGame();
}
void Minesweeper::slot_actionSelf_triggered()
{
    DialogSelf->show();
}
void Minesweeper::slot_actionUser_defined_triggered()
{
    nRow=DialogSelf->getRow();
    nColumn=DialogSelf->getCol();
    nBomb=DialogSelf->getBomb();

    if(0<nRow&&nRow<=24&&0<nColumn&&nColumn<=30&&0<nBomb&&nBomb<=666&&nBomb<nRow*nColumn)
        InitGame();
    else
    {
        QMessageBox::StandardButton ret;
        //弹出窗口提示
        ret=QMessageBox::critical(nullptr,"错误","行数范围0~24\n列数范围0~30\n炸弹数范围0~666\n（炸弹数小于格子数）",QMessageBox::Ok);
        nRow=9;
        nColumn=9;
        nBomb=10;
        InitGame();
    }
}

void Minesweeper::slot_sectime()
{
    sectime++;
    update();
}
